package com.mushuilingfeng.ddd.user.domain.user.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.mushuilingfeng.ddd.framework.AsSpringBean;
import com.mushuilingfeng.ddd.framework.factory.ComponentFactory;
import com.mushuilingfeng.ddd.framework.factory.DomainRepositoryFactory;
import com.mushuilingfeng.ddd.framework.factory.DomainServiceFactory;
import com.mushuilingfeng.ddd.user.domain.base.BusinessException;
import com.mushuilingfeng.ddd.user.domain.base.Ii8nCode;
import com.mushuilingfeng.ddd.user.domain.base.VerifyCodeType;
import com.mushuilingfeng.ddd.user.domain.base.service.VerifyCodeService;
import com.mushuilingfeng.ddd.user.domain.user.IUserLoginFilter;
import com.mushuilingfeng.ddd.user.domain.user.dto.AbstractLoginDto;
import com.mushuilingfeng.ddd.user.domain.user.dto.MobileLoginDto;
import com.mushuilingfeng.ddd.user.domain.user.dto.PasswordLoginDto;
import com.mushuilingfeng.ddd.user.domain.user.entity.User;
import com.mushuilingfeng.ddd.user.domain.user.repository.IUserRepository;
import com.mushuilingfeng.ddd.user.domain.user.service.IPasswordService;
import com.mushuilingfeng.ddd.user.domain.user.service.IUserService;
import lombok.extern.slf4j.Slf4j;

import java.util.Comparator;
import java.util.List;

/**
 * @author
 * @date 2024/9/27
 */
@AsSpringBean
@Slf4j
public class UserServiceImpl implements IUserService {
    private final int maxLoginErrorTimes = 3;
    
    private void executeFilters(AbstractLoginDto dto, User user) {
        List<IUserLoginFilter> userLoginFilterList = ComponentFactory.getComponentList(IUserLoginFilter.class);
        userLoginFilterList.sort(Comparator.comparingInt(IUserLoginFilter::order));
        if (!CollUtil.isEmpty(userLoginFilterList)) {
            userLoginFilterList.forEach(filter -> filter.filter(user, dto));
        }
    }
    
    @Override
    public User passwordLogin(PasswordLoginDto dto) {
        String account = dto.getAccount();
        String password = dto.getPassword();
        // 判断账号是否是多次登陆失败，被锁
        LoginTimeErrorChecker loginTimeErrorChecker = new LoginTimeErrorChecker(account, maxLoginErrorTimes);
        loginTimeErrorChecker.checkLoginAccountErrorTimes();
        User user = DomainRepositoryFactory.getRepository(IUserRepository.class).findByLoginAccount(account);
        if (null == user || !DomainServiceFactory.getDomainService(IPasswordService.class).verify(password, user.getLoginAccount().getLoginPassword())) {
            // 记录登陆错误次数
            loginTimeErrorChecker.recordLoginErrorTimes();
            throw new BusinessException(Ii8nCode.ACCOUNT_OR_PASSWORD_ERROR);
        }
        // 把一些敏感的数据隐藏
        user.mark();
        // 过滤器执行
        executeFilters(dto, user);
        // 清除错误次数
        loginTimeErrorChecker.clearLoginErrorTimes();
        return user;
    }
    
    @Override
    public void sendLoginVerifyCode(String tel) {
        DomainServiceFactory.getDomainService(VerifyCodeService.class).sendVerifyCode(tel, VerifyCodeType.LOGIN);
    }
    
    @Override
    public User mobileLogin(MobileLoginDto dto) {
        if (!DomainServiceFactory.getDomainService(VerifyCodeService.class).verifyCode(dto.getTelephone(), VerifyCodeType.LOGIN, dto.getVerifyCode())) {
            throw new BusinessException(Ii8nCode.VERIFY_CODE_ERROR);
        }
        // 对验证码进行验证
        User user = DomainRepositoryFactory.getRepository(IUserRepository.class).findByTelephone(dto.getTelephone());
        if (null == user) {
            throw new BusinessException(Ii8nCode.USER_NOT_EXIST);
        }
        String loginAccount = user.getLoginAccount().getLoginAccount();
        // 把一些敏感的数据隐藏
        user.mark();
        // 过滤器执行
        executeFilters(dto, user);
        // 清除错误次数
        DomainRepositoryFactory.getRepository(IUserRepository.class).clearLoginErrorTimes(loginAccount);
        return user;
    }
    
    @Override
    public void lockUser(String userAccount, String remark) {
        log.info("锁账号！！！");
        User user = DomainRepositoryFactory.getRepository(IUserRepository.class).findByLoginAccount(userAccount);
        if (user != null) {
            user.lock();
        }
    }
    
    private static class LoginTimeErrorChecker {
        private final String account;
        private final int maxLoginErrorTimes;
        private int errorTimes;
        
        LoginTimeErrorChecker(String account, int maxLoginErrorTimes) {
            this.account = account;
            this.maxLoginErrorTimes = maxLoginErrorTimes;
        }
        
        /**
         * 校验账号是否是多次登陆失败，被锁
         */
        void checkLoginAccountErrorTimes() {
            // 账号校验
            errorTimes = DomainRepositoryFactory.getRepository(IUserRepository.class).getLoginErrorTimes(account);
            if (errorTimes > maxLoginErrorTimes) {
                throw new BusinessException(Ii8nCode.LOGIN_MAX_ERROR);
            }
        }
        
        void clearLoginErrorTimes() {
            if (errorTimes == 0) {
                return;
            }
            DomainRepositoryFactory.getRepository(IUserRepository.class).clearLoginErrorTimes(account);
        }
        
        /**
         * 记录登陆错误次数
         */
        void recordLoginErrorTimes() {
            // 记录登陆错误次数
            DomainRepositoryFactory.getRepository(IUserRepository.class).incrementLoginErrorTimes(account);
        }
    }
    
}
